﻿using System.Collections;
using System.Drawing;

using Storm.TextEditor.Editor;
using Storm.TextEditor.Parser.Objects.Collections;
using Storm.TextEditor.Parser.XML;

namespace Storm.TextEditor.Parser.Objects
{
	/// <summary>
	/// Represents a block of code in a document.
	/// </summary>
	/// <remarks>
	/// The Block class represents a specific code/text element<br/>
	/// such as a string, comment or the code itself.<br/>
	/// <br/>
	/// A Block can contain keywords, operators, scopes and child Blocks.<br/>
	/// <br/>
	/// <br/>
	/// For example, if we were to describe the language C#,<br/>
	/// we would have the following blocks:<br/>
	/// <br/>
	/// Code block						- the Block containing all the keywords and operators.<br/>
	/// Singleline comment block		- a Block that starts on // - terminates at the end of a line.<br/>
	/// Multiline comment block			- a Block that starts on /* - can span multiple rows and terminates on */.<br/>
	/// String block					- a Block that starts on "  - terminates on " or at the end of a line.<br/>
	/// Char block						- a Block that starts on '  - terminates on ' or at the end of a line.<br/>
	/// <br/>
	/// <b>CHILD BLOCKS:</b><br/>
	/// The main Block would have all other blocks as child blocks, since they can only appear inside the<br/>
	/// Block. A string can for example never exist inside a comment in C#.<br/>
	/// A Block can also have itself as child block.<br/>
	/// For example, the C# Block can have itself as a child block and use the scope patterns "{" and "}".<br/>
	/// This way we can accomplish FOLDING since the parser will know where a new scope starts and ends.<br/>
	/// <br/>
	/// <b>SCOPES:</b><br/>
	/// Scopes describe what patterns starts and what patterns end a specific Block.<br/>
	/// For example, the C# multiline comment have the scope patterns /* and */.<br/>
	/// <br/>
	/// <b>KEYWORDS:</b><br/>
	/// A Keyword is a pattern that can only exist between separator chars.<br/>
	/// For example the keyword "for" in C# is valid if it is contained in something like: "for (...)". </br>
	/// <br/>
	/// <b>OPERATORS:</b><br/>
	/// Operators is the same thing as keywords but are valid even if there are no separator chars around them.<br/>
	/// In most cases operators are only one or two chars such as ":" or "->"<br/>
	/// Operators in this context should not be mixed up with code operators such as "and" or "xor" in VB6.<br/>
	/// In this context they are keywords.<br/>
	public class Block
	{
		#region Fields

		private PatternListList keywordsList  = null;
		private PatternListList operatorsList = null;

		private ScopeCollection scopePatterns = null;
		private BlockCollection childBlocks   = new BlockCollection();

		private TextStyle style     = null;
		private Color     backColor = Color.Transparent;

		private string name              = "";
		private bool   multiline         = false;
		private bool   terminateChildren = false;

		private Language  parent      = null;
		private Hashtable lookupTable = new Hashtable();

		private PatternCollection simplePatterns  = new PatternCollection();
		private PatternCollection complexPatterns = new PatternCollection();

		#endregion

		#region Properties

		/// <summary>
		/// Gets the simple patterns.
		/// </summary>
		/// <value>The simple patterns.</value>
		public PatternCollection SimplePatterns
		{
			get { return simplePatterns; }
		}

		/// <summary>
		/// Gets whether the background color of the Block is transparent.
		/// </summary>
		public bool Transparent
		{
			get { return (this.backColor.A == 0); }
		}

		/// <summary>
		/// Gets or sets the language parent of the Block.
		/// </summary>
		public Language Parent
		{
			get { return parent; }
			set { parent = value; }
		}

		/// <summary>
		/// A list of keyword groups.
		/// For example, one keyword group could be "keywords" and another could be "datatypes".
		/// These groups could have different color shemes assigned to them.
		/// </summary>
		public PatternListList KeywordsList
		{
			get { return keywordsList; }
			set { keywordsList = value; }
		}

		/// <summary>
		/// A list of operator groups.
		/// Each operator group can contain its own operator patterns and its own color shemes.
		/// </summary>
		public PatternListList OperatorsList
		{
			get { return operatorsList; }
			set { operatorsList = value; }
		}

		/// <summary>
		/// A list of scopes, most blocks only contain one scope, eg. a scope with start and end patterns for example "/*" and "*/"
		/// for multiline comments, but in some cases you will need more scopes, eg. PHP uses both "&lt;?", "?&gt;" and "&lt;?PHP", "PHP?&gt;".
		/// </summary>
		public ScopeCollection ScopePatterns
		{
			get { return scopePatterns; }
			set { scopePatterns = value; }
		}

		/// <summary>
		/// A list containing which Blocks are valid child blocks in a specific block.
		/// eg. strings and comments are child blocks for a Block.
		/// </summary>
		public BlockCollection ChildBlocks
		{
			get { return childBlocks; }
			set { childBlocks = value; }
		}

		/// <summary>
		/// The style to use when colorizing the content of a block,
		/// meaning everything in this block except keywords, operators and ChildBlocks.
		/// </summary>
		public TextStyle Style
		{
			get { return style; }
			set { style = value; }
		}

		/// <summary>
		/// The name of this block.
		/// Names are not required for blocks but can be a good help when interacting with the parser.
		/// </summary>
		public string Name
		{
			get { return name; }
			set { name = value; }
		}

		/// <summary>
		/// The background color of a block.
		/// </summary>
		public Color BackColor
		{
			get { return backColor; }
			set { backColor = value; }
		}

		/// <summary>
		/// Gets or sets whether the Block can span multiple lines or if it should terminate at the end of a line.
		/// </summary>
		public bool Multiline
		{
			get { return multiline; }
			set { multiline = value; }
		}

		/// <summary>
		/// Gets or sets if the parser should terminate any ChildBlock when it finds an end scope Pattern for this block.
		/// For example %&gt; in ASP terminates any ASP block, even if it appears inside an ASP string.
		/// </summary>
		public bool TerminateChildren
		{
			get { return terminateChildren; }
			set { terminateChildren = value; }
		}

		/// <summary>
		/// Gets or sets the lookup table of the Block.
		/// </summary>
		public Hashtable LookupTable
		{
			get { return lookupTable; }
			set { lookupTable = value; }
		}

		/// <summary>
		/// Gets or sets the collection of complex Patterns in the Block.
		/// </summary>
		public PatternCollection ComplexPatterns
		{
			get { return complexPatterns; }
			set { complexPatterns = value; }
		}

		#endregion

		#region Methods

		#region Public

		/// <summary>
		/// Resets the lookup table of the Block.
		/// </summary>
		public void ResetLookupTable()
		{
			if (lookupTable != null)
				lookupTable.Clear();

			simplePatterns.Clear();
			complexPatterns.Clear();
		}

		/// <summary>
		/// Adds a Pattern to the lookup table of the Block.
		/// </summary>
		/// <param name="pattern">The Pattern to add.</param>
		public void AddToLookupTable(Pattern pattern)
		{
			if (pattern.IsComplex == true)
				complexPatterns.Add(pattern);
			else
				simplePatterns.Add(pattern);
		}

		/// <summary>
		/// Builds the lookup table of the Block.
		/// </summary>
		public void BuildLookupTable()
		{
			PatternComparer comparer = new PatternComparer();
			simplePatterns.Sort(comparer);

			foreach (Pattern pattern in simplePatterns)
			{
				if (pattern.StringPattern.Length <= 2)
				{
					char ch = pattern.StringPattern[0];

					if (pattern.Parent.CaseSensitive == false)
					{
						char ch1 = char.ToLower(ch);
						if (lookupTable[ch1] == null)
							lookupTable[ch1] = new PatternCollection();

						PatternCollection patterns = lookupTable[ch1] as PatternCollection;
						if (patterns.Contains(pattern) == false)
							patterns.Add(pattern);

						char ch2 = char.ToUpper(ch);
						if (lookupTable[ch2] == null)
							lookupTable[ch2] = new PatternCollection();

						patterns = lookupTable[ch2] as PatternCollection;
						if (patterns.Contains(pattern) == false)
							patterns.Add(pattern);
					}
					else
					{
						if (lookupTable[ch] == null)
							lookupTable[ch] = new PatternCollection();

						PatternCollection patterns = lookupTable[ch] as PatternCollection;
						if (patterns.Contains(pattern) == false)
							patterns.Add(pattern);
					}
				}
				else
				{
					string stringPattern = pattern.StringPattern.Substring(0, 3).ToLower();

					if (lookupTable[stringPattern] == null)
						lookupTable[stringPattern] = new PatternCollection();

					PatternCollection patterns = lookupTable[stringPattern] as PatternCollection;
					if (patterns.Contains(pattern) == false)
						patterns.Add(pattern);
				}
			}
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of Block.
		/// </summary>
		public Block()
		{
			keywordsList  = new PatternListList(this);
			operatorsList = new PatternListList(this);

			style = new TextStyle();

			keywordsList.Parent = this;
			keywordsList.IsKeyword = true;

			operatorsList.Parent = this;
			operatorsList.IsOperator = true;
			scopePatterns = new ScopeCollection(this);
		}

		/// <summary>
		/// Initializes a new instance of Block.
		/// </summary>
		/// <param name="parent">Language parent of the Block.</param>
		public Block(Language parent)
			: this()
		{
			this.Parent = parent;
			this.Parent.ChangeVersion();
		}
	}
}
